home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Plus 1995 #3 & #4
/
Amiga Plus CD - 1995 - No. 3 and 4.iso
/
pd
/
sound
/
cybersound
/
14bit.driver
/
source
/
driver.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-07-20
|
17KB
|
741 lines
/*****************************************************************************
*
* CyberSound: 14 Bit sound driver
*
* (c) 1995 by Christian Buchner
*
*****************************************************************************
*
* Driver.c
*/
#include <exec/interrupts.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <graphics/displayinfo.h>
#include <intuition/intuitionbase.h>
#include <devices/audio.h>
#include <hardware/custom.h>
#include <hardware/intbits.h>
#include <hardware/dmabits.h>
#include <hardware/adkbits.h>
#include <hardware/cia.h>
#include <clib/alib_protos.h>
#include <proto/graphics.h>
#include <proto/dos.h>
#include <stdarg.h>
#include "Driver.h"
#include "DriverBase.h"
void KPrintF(UBYTE *fmt,...);
#define DB(x)
/*****************************************************************************/
#define TABLE_SIZE (65536*sizeof(UWORD)) /* 2^16 UWORDS */
/*****************************************************************************/
#define DMABUF_SIZE (4*BUFFER_SIZE) /* The byte size of one audio buffer */
#define LEFT_MSB (0*BUFFER_SIZE)
#define RIGHT_MSB (1*BUFFER_SIZE)
#define RIGHT_LSB (2*BUFFER_SIZE)
#define LEFT_LSB (3*BUFFER_SIZE)
/* Buffer layout:
1) Left channel MSB Bytes -> Audio Channel 1
2) Right channel MSB Bytes -> Audio Channel 2
3) Right channel LSB Bytes -> Audio Channel 3
4) Left channel LSB Bytes -> Audio Channel 4
*/
/*****************************************************************************/
#define AUDIO_INTERRUPTS (INTF_AUD0|INTF_AUD1|INTF_AUD2|INTF_AUD3)
#define INTERRUPT_USED (INTF_AUD3)
#define INTERRUPTS_NOT_USED (INTF_AUD0|INTF_AUD1|INTF_AUD2)
/*****************************************************************************/
BOOL InitDriver( struct DriverBase *db )
{
struct Custom *custom = (struct Custom*)0xdff000;
UBYTE ChannelMap[]={0xf};
BOOL LoadSuccess=FALSE;
BPTR FH;
UWORD i;
DB(KPrintF("\n>>> INITIALIZATION <<<\n"));
if (AudioReply=CreateMsgPort())
{
if (AudioRequest=CreateIORequest(AudioReply,sizeof(struct IOAudio)))
{
AudioRequest->ioa_Request.io_Message.mn_Node.ln_Pri=127;
AudioRequest->ioa_Data=ChannelMap;
AudioRequest->ioa_Length=sizeof(ChannelMap);
AudioRequest->ioa_Request.io_Flags=ADIOF_NOWAIT;
if (!OpenDevice("audio.device",0,(struct IORequest*)AudioRequest,0))
{
if (ConversionTable=AllocVec(TABLE_SIZE,MEMF_ANY))
{
if (AdditiveArray=AllocVec(256,MEMF_ANY))
{
if (FH=Open("ENV:CyberSound/SoundDrivers/14Bit_Calibration",MODE_OLDFILE))
{
if (Read(FH,AdditiveArray,256)==256)
{
LoadSuccess=TRUE;
}
Close(FH);
}
if (!LoadSuccess)
{
for (i=0;i<255;i++) AdditiveArray[i]=0x55;
AdditiveArray[255]=0x7f;
}
CreateTable(ConversionTable,AdditiveArray);
CurrentFormat=STREAM_16BIT_MOTOROLA;
AudioVector.is_Node.ln_Type=
NOPVector.is_Node.ln_Type=NT_INTERRUPT;
AudioVector.is_Node.ln_Name=
NOPVector.is_Node.ln_Name="14 bit driver";
AudioVector.is_Data=
NOPVector.is_Data=db;
AudioVector.is_Code=(void*)(&AudioInterrupt);
NOPVector.is_Code=(void*)(&NOPInterrupt);
custom->dmacon= DMAF_AUDIO;
custom->adkcon= ADKF_USE3PN|ADKF_USE2P3|
ADKF_USE1P2|ADKF_USE0P1|
ADKF_USE3VN|ADKF_USE2V3|
ADKF_USE1V2|ADKF_USE0V1;
OldAudVector0=SetIntVector(INTB_AUD0,&NOPVector);
OldAudVector1=SetIntVector(INTB_AUD1,&NOPVector);
OldAudVector2=SetIntVector(INTB_AUD2,&NOPVector);
OldAudVector3=SetIntVector(INTB_AUD3,&AudioVector);
custom->intena=custom->intreq=AUDIO_INTERRUPTS;
custom->intena=INTF_SETCLR|INTERRUPT_USED;
NestLevel=0;
DriverFlags = FLGF_NEEDIRQ;
if (SetBuffers(1024,16,db))
{
return(TRUE);
}
}
}
}
}
}
DeInitDriver(db);
return(FALSE);
}
/*****************************************************************************/
void DeInitDriver( struct DriverBase *db )
{
struct Custom *custom = (struct Custom*)0xdff000;
DB(KPrintF("\n>>> DE-INITIALIZATION <<<\n"));
if (AudioRequest)
{
if (AudioRequest->ioa_Request.io_Device)
{
FreeBuffers(db);
custom->intena=custom->intreq=AUDIO_INTERRUPTS;
if (OldAudVector0) SetIntVector(INTB_AUD0,OldAudVector0);
if (OldAudVector1) SetIntVector(INTB_AUD1,OldAudVector1);
if (OldAudVector2) SetIntVector(INTB_AUD2,OldAudVector2);
if (OldAudVector3) SetIntVector(INTB_AUD3,OldAudVector3);
if (AdditiveArray)
{
FreeVec(AdditiveArray);
AdditiveArray=NULL;
}
if (ConversionTable)
{
FreeVec(ConversionTable);
ConversionTable=NULL;
}
CloseDevice((struct IORequest*)AudioRequest);
AudioRequest->ioa_Request.io_Device=NULL;
}
DeleteIORequest(AudioRequest);
AudioRequest=NULL;
}
if (AudioReply)
{
DeleteMsgPort(AudioReply);
AudioReply=NULL;
}
}
/*****************************************************************************/
BOOL __asm SetBuffers( register __d0 ULONG audiosize, register __d1 ULONG queuesize, register __a6 struct DriverBase *db)
{
BOOL Success=FALSE;
FreeBuffers(db);
BUFFER_SIZE=audiosize;
QUEUE_SIZE=queuesize;
if (DMABuffer1=AllocVec(DMABUF_SIZE,MEMF_CHIP))
{
if (DMABuffer2=AllocVec(DMABUF_SIZE,MEMF_CHIP))
{
if (QueueBuffer=AllocVec(QUEUE_SIZE*sizeof(struct StreamInfo),MEMF_CLEAR))
{
/* Flush (Initialize!) stream */
FlushStream(db);
Success=TRUE;
}
}
}
if (!Success) FreeBuffers(db);
return(Success);
}
/*****************************************************************************/
void __asm FreeBuffers( register __a6 struct DriverBase *db )
{
FlushStream(db);
if (DMABuffer1)
{
FreeVec(DMABuffer1);
DMABuffer1=NULL;
}
if (DMABuffer2)
{
FreeVec(DMABuffer2);
DMABuffer2=NULL;
}
if (QueueBuffer)
{
FreeVec(QueueBuffer);
QueueBuffer=NULL;
}
BUFFER_SIZE=0;
QUEUE_SIZE=0;
}
/*****************************************************************************/
BOOL __asm StreamFormat( register __d0 ULONG format, register __a6 struct DriverBase *db )
{
BOOL Success=FALSE;
FlushStream(db);
if ( (format==STREAM_16BIT_INTEL && CurrentFormat==STREAM_16BIT_MOTOROLA) ||
(format==STREAM_16BIT_MOTOROLA && CurrentFormat==STREAM_16BIT_INTEL ) )
{
SwapEndian(ConversionTable);
CurrentFormat=format;
Success=TRUE;
}
return(Success);
}
/*****************************************************************************/
ULONG __asm AskFrequency( register __d0 ULONG frequency, register __a6 struct DriverBase *db )
{
UWORD period;
ULONG result;
period=AskPeriod(frequency,db);
result=(*(struct ExecBase**)(4))->ex_EClockFrequency*5/period;
return(result);
}
/*****************************************************************************/
ULONG __asm AskPeriod( register __d0 ULONG frequency, register __a6 struct DriverBase *db )
{
UWORD period;
ULONG ModeID;
struct MonitorInfo mon;
/* See if front screen has changed */
if (IntuitionBase->FirstScreen != CurrentScreen)
{
/* Store as new current screen and check for NULL ptr */
if (CurrentScreen=IntuitionBase->FirstScreen)
{
/* Get mode ID */
ModeID=GetVPModeID(&CurrentScreen->ViewPort);
/* See if current mode ID has changed */
if (ModeID != CurrentModeID)
{
/* Store as new current Mode ID */
CurrentModeID=ModeID;
/* Get information on current Mode ID */
GetDisplayInfoData(NULL,(UBYTE*)&mon,sizeof(mon),DTAG_MNTR,ModeID);
/* Calculate minimal period value for current ModeID */
CurrentMinPer=mon.TotalColorClocks*mon.TotalRows/(2*(mon.TotalRows-mon.MinRow+1));
}
}
}
period=((*(struct ExecBase**)(4))->ex_EClockFrequency*5+(frequency/2))/frequency;
if (period<CurrentMinPer) period=CurrentMinPer;
return(period);
}
/*****************************************************************************/
BOOL __asm ProvideStream( register __a0 UWORD *left,
register __a1 UWORD *right,
register __d0 ULONG samples,
register __d1 UWORD interleave,
register __d2 ULONG frequency,
register __a2 void (*callback)(void),
register __a6 struct DriverBase *db)
{
struct Custom *custom = (struct Custom*)0xdff000;
struct StreamInfo *SI;
BOOL Success=FALSE;
DB(KPrintF("***SUPPLY(%ld) ",samples));
/* Only supply if there are buffers to store it */
if (QueueBuffer && DMABuffer1 && DMABuffer2)
{
/* check if there is enough room in the queue */
if (((ProvidePos+1)%QUEUE_SIZE)!=PlayPos)
{
/* Arbitrate list by disabling the Audio Interrupt */
SD_Lock(db);
SI= &QueueBuffer[ProvidePos];
SI->si_left=left;
SI->si_right=right;
SI->si_samples=samples;
SI->si_interleave=interleave;
SI->si_period=AskPeriod(frequency,db);
SI->si_callback=callback;
SI->si_offset=0;
ProvidePos=(ProvidePos+1)%QUEUE_SIZE;
/* Cause an audio interrupt if needed */
if (DriverFlags & FLGF_NEEDIRQ)
{
DB(KPrintF("CAUSE "));
DriverFlags &= (~FLGF_NEEDIRQ);
custom->intreq = INTF_SETCLR|INTERRUPT_USED;
}
/* Re-enable Audio Interrupt */
SD_Unlock(db);
Success=TRUE;
}
else
{
DB(KPrintF("QUEUE FULL! "));
}
}
else
{
DB(KPrintF("NO QUEUE! "));
}
DB(KPrintF("\n"));
return(Success);
}
/*****************************************************************************/
void __asm FlushStream( register __a6 struct DriverBase *db )
{
struct Custom *custom = (struct Custom*)0xdff000;
DB(KPrintF("***FLUSH "));
/* Only flush if there is a queue to flush */
if (QueueBuffer)
{
DB(KPrintF("CAUSE "));
/* Set FLUSH flag and cause an audio interrupt */
DriverFlags |= FLGF_FLUSH;
custom->intreq=INTF_SETCLR|INTERRUPT_USED;
/* Problem: Interrupt may occur somewhat delayed ;-( */
/* This might speed it up a bit ;-) */
custom->aud[0].ac_per=
custom->aud[1].ac_per=
custom->aud[2].ac_per=
custom->aud[3].ac_per=1;
}
DB(KPrintF("\n"));
}
/*****************************************************************************/
void __asm PauseStream( register __a6 struct DriverBase *db )
{
DB(KPrintF("***PAUSE "));
/* set pause flag */
DriverFlags |= FLGF_PAUSE;
DB(KPrintF("\n"));
}
/*****************************************************************************/
void __asm ResumeStream( register __a6 struct DriverBase *db )
{
struct Custom *custom = (struct Custom*)0xdff000;
DB(KPrintF("***RESUME "));
/* clear pause flag */
DriverFlags &= ~(FLGF_PAUSE);
/* cause an audio interrupt if needed */
if (DriverFlags & FLGF_NEEDIRQ)
{
DB(KPrintF("CAUSE "));
DriverFlags &= (~FLGF_NEEDIRQ);
custom->intreq = INTF_SETCLR|INTERRUPT_USED;
}
DB(KPrintF("\n"));
}
/*****************************************************************************/
void __asm AudioInterrupt( register __d1 UWORD ActiveInt, register __a0 struct Custom * custom, register __a1 struct DriverBase *db, register __a6 SysBase )
{
struct StreamInfo *SI;
UBYTE *Bufferptr;
ULONG Chunklen,Playlen;
UWORD Period=0;
ULONG Pos;
struct CIA *ciaa=(struct CIA*)0xbfe001;
/* clear interrupt request bits */
custom->intreq=(ActiveInt & INTERRUPT_USED);
/* switch off audio filter */
ciaa->ciapra |= CIAF_LED;
DB(KPrintF("***IRQ "));
/* Set period of the current DMA cycle */
if (DriverFlags & FLGF_PLAYING)
{
custom->aud[0].ac_per=
custom->aud[1].ac_per=
custom->aud[2].ac_per=
custom->aud[3].ac_per=LastPeriod;
}
else
{
/* Not playing? We we need an IRQ to restart */
DriverFlags |= FLGF_NEEDIRQ;
}
/* Delete blocks that have been played completely */
/* and delete ALL blocks if the FLUSH bit is set */
for(PlayPos;PlayPos!=ProvidePos;PlayPos=(PlayPos+1)%QUEUE_SIZE)
{
SI= &QueueBuffer[PlayPos];
if ((SI->si_offset == 0xffffffff) || (DriverFlags&FLGF_FLUSH))
{
DB(KPrintF("DELBLK "));
/* See if this was the last block or if the FLUSH bit is set */
if ((((PlayPos+1)%QUEUE_SIZE)==ProvidePos) || (DriverFlags&FLGF_FLUSH))
{
/* Stop DMA, nothing more to play */
StopDMA(db);
}
/* Call callback hook */
if (SI->si_callback) (SI->si_callback)();
}
else break;
}
/* Some more actions to take on FLUSH request */
if (DriverFlags & FLGF_FLUSH)
{
DB(KPrintF("FLUSHED "));
/* Reset pointers */
CurrentBuffer=1;
ProvidePos=0;
PlayPos=0;
/* Reset the flags bits */
DriverFlags &= ~(FLGF_FLUSH|FLGF_PLAYING);
DriverFlags |= FLGF_NEEDIRQ;
}
else
{
/* Mark blocks for deletion that are almost played */
for (Pos=PlayPos;Pos!=ProvidePos;Pos=(Pos+1)%QUEUE_SIZE)
{
SI= &QueueBuffer[Pos];
/* Block finished but still playing? */
if (SI->si_offset==SI->si_samples)
{
DB(KPrintF("MARKDEL "));
/* Mark block as deletable for next interrupt */
SI->si_offset= 0xffffffff;
/* See if we have a successor block */
if (((Pos+1)%QUEUE_SIZE)==ProvidePos)
{
DB(KPrintF("NEEDIRQ "));
/* No successor! We need an IRQ when one is provided */
DriverFlags |= FLGF_NEEDIRQ;
}
}
else break;
}
/* Actions to take on PAUSE request */
if (DriverFlags & FLGF_PAUSE)
{
/* And stop the DMA right now */
StopDMA(db);
}
else
{
/* Get current buffer pointer */
if (CurrentBuffer == 1)
{
DB(KPrintF("BUF1 "));
Bufferptr=DMABuffer1;
}
else
{
DB(KPrintF("BUF2 "));
Bufferptr=DMABuffer2;
}
/* Fill buffer until filled */
for (Playlen=0;(Playlen<BUFFER_SIZE) && (Pos!=ProvidePos);Pos=(Pos+1)%QUEUE_SIZE)
{
SI= &QueueBuffer[Pos];
/* get length of current block */
Chunklen = SI->si_samples-SI->si_offset;
if (Chunklen>0)
{
/* maximize chunklen to buffer size */
if (Chunklen > BUFFER_SIZE-Playlen)
{
Chunklen = BUFFER_SIZE-Playlen;
}
/* Make sure that period remains constant in one DMA cycle */
if (Period)
{
if (Period != SI->si_period) break;
}
else
{
Period=SI->si_period;
}
DB(KPrintF("CONVERT(%ld,%ld) ",Chunklen,(ULONG)Period));
/* Convert left channel */
ConvertStream( SI->si_left+SI->si_offset*(SI->si_interleave+1),
Bufferptr+LEFT_MSB+Playlen,
Bufferptr+LEFT_LSB+Playlen,
ConversionTable,
Chunklen,
SI->si_interleave);
/* Convert right channel */
ConvertStream( SI->si_right+SI->si_offset*(SI->si_interleave+1),
Bufferptr+RIGHT_MSB+Playlen,
Bufferptr+RIGHT_LSB+Playlen,
ConversionTable,
Chunklen,
SI->si_interleave);
/* Increment offset */
SI->si_offset+=Chunklen;
Playlen+=Chunklen;
}
}
if (Playlen)
{
DB(KPrintF("PLAY(%ld,%ld) ",Playlen,(ULONG)Period));
LastPeriod=Period;
custom->aud[0].ac_ptr=(UWORD*)(Bufferptr+LEFT_MSB);
custom->aud[1].ac_ptr=(UWORD*)(Bufferptr+RIGHT_MSB);
custom->aud[2].ac_ptr=(UWORD*)(Bufferptr+RIGHT_LSB);
custom->aud[3].ac_ptr=(UWORD*)(Bufferptr+LEFT_LSB);
custom->aud[0].ac_vol=
custom->aud[1].ac_vol=64;
custom->aud[2].ac_vol=
custom->aud[3].ac_vol=1;
custom->aud[0].ac_len=
custom->aud[1].ac_len=
custom->aud[2].ac_len=
custom->aud[3].ac_len=Playlen/2;
/* If not already playing, start to do so */
if (!(DriverFlags & FLGF_PLAYING))
{
DB(KPrintF("STARTDMA "));
DriverFlags |= FLGF_PLAYING;
custom->aud[0].ac_per=
custom->aud[1].ac_per=
custom->aud[2].ac_per=
custom->aud[3].ac_per=Period;
custom->dmacon = DMAF_SETCLR|DMAF_AUDIO;
}
/* Swap current buffer */
if (CurrentBuffer == 1)
{
CurrentBuffer=2;
}
else
{
CurrentBuffer=1;
}
}
}
}
DB(KPrintF("\n"));
}
/*****************************************************************************/
void __asm StopDMA( register __a6 struct DriverBase *db )
{
struct Custom *custom = (struct Custom*)0xdff000;
/* If DMA active, stop it right now */
if (DriverFlags&FLGF_PLAYING)
{
DB(KPrintF("STOPDMA "));
/* Reset driver flags */
DriverFlags &= (~FLGF_PLAYING);
DriverFlags |= FLGF_NEEDIRQ;
/* Stop the audio DMA */
custom->dmacon = DMAF_AUDIO;
custom->aud[0].ac_vol=
custom->aud[1].ac_vol=
custom->aud[2].ac_vol=
custom->aud[3].ac_vol=0;
}
}
/*****************************************************************************/
void __asm NOPInterrupt(register __d1 UWORD ActiveInt, register __a0 struct Custom * custom, register __a1 struct DriverBase *db, register __a6 SysBase)
{
custom->intreq=(ActiveInt & INTERRUPTS_NOT_USED);
}
/*****************************************************************************/
void __asm SD_Lock( register __a6 struct DriverBase *db )
{
struct Custom *custom = (struct Custom*)0xdff000;
if (NestLevel==0)
{
custom->intena=INTERRUPT_USED;
}
NestLevel++;
}
/*****************************************************************************/
void __asm SD_Unlock( register __a6 struct DriverBase *db )
{
struct Custom *custom = (struct Custom*)0xdff000;
if (NestLevel!=0)
{
NestLevel--;
if (NestLevel==0)
{
custom->intena=INTF_SETCLR|INTERRUPT_USED;
}
}
}